package cn.benma666.sjsj.web;

import cn.benma666.constants.UtilConstInstance;
import cn.benma666.dict.Cllx;
import cn.benma666.dict.Ljpd;
import cn.benma666.domain.SysPtglXtxx;
import cn.benma666.domain.SysQxYhxx;
import cn.benma666.exception.MyException;
import cn.benma666.iframe.BasicObject;
import cn.benma666.iframe.MyParams;
import cn.benma666.iframe.Result;
import cn.benma666.myutils.StringUtil;
import cn.benma666.vo.WebSocketKhdxxVo;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 系统消息
 */
@Component
@ServerEndpoint("/xtxx/{token}")
@Getter
@Setter
public class XtxxWebSocket extends BasicObject implements UtilConstInstance {
    public XtxxWebSocket(){
        log.trace("websocket实例化");
    }

    /**
     * 系统消息推送会话MAP
     */
    private static final Map<String, XtxxWebSocket> xtxxMap = new ConcurrentHashMap<>();
    /**
     * 监听目的地集合
     */
    private final Set<String> jtmddSet = Collections.synchronizedSet(new HashSet<>());
    /**
     *  与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 会话id
     */
    private String token;
    /**
     * 当前会话对应的用户
     */
    private SysQxYhxx user;
    /**
     * 系统消息参数对象
     */
    private static MyParams xtxxParams;
    /**
     *  连接建立成功调用的方法
     * @param session  可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        token = session.getId();
        user = UserManager.getUser(session.getPathParameters().get("token"));
        //系统消息注册，默认每个人都有
        jtmddSet.add(SysPtglXtxx.SYS_PTGL_XTXX);
        synchronized (xtxxMap){
            xtxxMap.put(token,this);
            log.info("新增一连接：{},{}！当前在线人数为{}",user.getYhxm(),token, getOnlineCount());
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(){
        synchronized (xtxxMap) {
            xtxxMap.remove(token);
            if(user!=null){
                log.info("有一连接关闭：{}，{}！当前在线人数为{}", user.getYhxm(),token, getOnlineCount());
            }else {
                log.warn("有一连接关闭：{}！当前在线人数为{},用户信息为空", token, getOnlineCount());
            }
        }
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.debug("来自客户端的消息:"+message);
        WebSocketKhdxxVo wsk = JSON.parseObject(message, WebSocketKhdxxVo.class);
        wsk.setToken(token);
        MyParams jcxx = LjqManager.jcxxByDxdm(wsk.getMdddl(), user);
        //设置客户端消息
        jcxx.sys().setWskhdxx(wsk);
        Result r;
        try{
            switch (wsk.getXxlb()){
                case "1":
                    //注册目的地
                    jtmddSet.add(wsk.getMdd());
                    //设置处理类型
                    jcxx.sys().setCllx(Cllx.wsmddzc.name());
                    r = LjqManager.data(jcxx);
                    break;
                case "2":
                    //取消目的地
                    jtmddSet.remove(wsk.getMdd());
                    //设置处理类型
                    jcxx.sys().setCllx(Cllx.wsmddqx.name());
                    r = LjqManager.data(jcxx);
                    break;
                default:
                    throw new MyException("暂不支持的消息类型："+wsk);
            }
        }catch (MyException t){
            r = t.getResult();
        }catch (Throwable t){
            r = failed(t.getMessage());
            log.error(r.getMsg(),t);
        }
        if(!r.isStatus()){
            //回调失败
            SysPtglXtxx xx = SysPtglXtxx.builder().xxnr(wsk.getMdd() + "：消息处理失败》"+r.getMsg())
                            .mdddl(wsk.getMdddl()).mddxl(wsk.getMddxl()).tshh(token).build();
            XtxxWebSocket.sendMsg(xx,user);
        }
    }

    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error){
        log.trace("websocket发生错误:{},用户：{}",token,user==null?null:user.getYhdm(),error);
//        SysPtglXtxx xx = new SysPtglXtxx("异常：" + error.getMessage());
//        xx.setTshh(token);
//        sendMsg(xx,user);
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解，是根据自己需要添加的方法。
     */
    public void sendMessage(SysPtglXtxx xx) throws IOException {
        this.session.getBasicRemote().sendText(success("正常推送",xx).toString());
    }

    /**
     * 通过websoket向前端推送消息
     * @param xtxx 消息体
     * @param user 推送人
     */
    public synchronized static void sendMsg(SysPtglXtxx xtxx, SysQxYhxx user){
        slog.trace("开始推送消息："+xtxx);
        if(!SysPtglXtxx.XXLB_SXXX.equals(xtxx.getXxlb())&&isBlank(xtxx.getTshh())
                &&SysPtglXtxx.SYS_PTGL_XTXX.equals(xtxx.getMdd())){
            //非要求客户端刷新的消息 且 推送目的地为空，系统消息 且 非按会话推送
            //添加到消息表
            xtxx.setYdzt(Ljpd.FALSE.getCode());
            xtxx.setId(StringUtil.getUUIDUpperStr());
            getXtxxParams().put(LjqInterface.KEY_USER,user);
            xtxxParams.put(LjqInterface.KEY_YOBJ,xtxx);
            LjqManager.data(xtxxParams);
        }
        try {
            List<XtxxWebSocket> xxList = new ArrayList<>(xtxxMap.values());
            if(!isBlank(xtxx.getTshh())){
                //推送会话
                xxList = new ArrayList<>();
                XtxxWebSocket xx = xtxxMap.get(xtxx.getTshh());
                if(xx==null){
                    slog.debug("推送会话不存在：{},{}",xtxx.getTshh(),xtxx);
                    return;
                }
                xxList.add(xx);
            }
            Iterator<XtxxWebSocket> it;
            XtxxWebSocket item;
            if(!isBlank(xtxx.getJsr())){
                it = xxList.iterator();
                while (it.hasNext()){
                    item = it.next();
                    if(!item.user.getId().equals(xtxx.getJsr())){
                        it.remove();
                    }
                }
            }
            if(!isBlank(xtxx.getJsdw())){
                it = xxList.iterator();
                while (it.hasNext()){
                    item = it.next();
                    if(!item.user.getSsjg().equals(xtxx.getJsdw())){
                        it.remove();
                    }
                }
            }
            if(!isBlank(xtxx.getMdd())){
                it = xxList.iterator();
                while (it.hasNext()){
                    item = it.next();
                    if(!item.jtmddSet.contains(xtxx.getMdd())){
                        it.remove();
                    }
                }
            }
            for(XtxxWebSocket i : xxList){
                i.sendMessage(xtxx);
            }
            slog.trace("成功向"+xxList.size()+"人推送消息");
        }catch (IOException e){
            slog.trace("向推送消息失败",e);
        }catch (Exception e){
            throw new MyException("推送消息异常",e);
        }
    }
    /**
     * @return 在线窗口数
     */
    public static int getOnlineCount() {
        return xtxxMap.size();
    }

    /**
     * @return 在线会话数
     */
    public static int getOnLineHhs(){
        Set<String> s = new HashSet<>();
        return (int) xtxxMap.entrySet().stream().filter(e->{
            if(s.contains(e.getValue().getUser().getToken())){
                return false;
            }
            s.add(e.getValue().getUser().getToken());
            return true;
        }).count();
    }

    /**
     * @return 在线用户数
     */
    public static int getOnLineYhs(){
        Set<String> s = new HashSet<>();
        return (int) xtxxMap.entrySet().stream().filter(e->{
            if(s.contains(e.getValue().getUser().getId())){
                return false;
            }
            s.add(e.getValue().getUser().getId());
            return true;
        }).count();
    }

    /**
     * @return 用户在线会话数，主要体现web端登录人员，不能体现后台接口访问用户
     */
    public static int getYhzxhhs(SysQxYhxx yh){
        Set<String> s = new HashSet<>();
        return (int) xtxxMap.entrySet().stream().filter(e->{
            if(!yh.getId().equals(e.getValue().getUser().getId())||s.contains(e.getValue().getUser().getToken())){
                return false;
            }
            s.add(e.getValue().getUser().getToken());
            return true;
        }).count();
    }

    /**
     * 基于会话id设置用户信息
     * @param token 会话id
     * @param user 用户信息
     */
    public static void setUserByToken(String token,SysQxYhxx user){
        xtxxMap.entrySet().forEach(e->{
            if(token.equals(e.getValue().getUser().getToken())){
                e.getValue().setUser(user);
            }
        });
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        XtxxWebSocket that = (XtxxWebSocket) o;
        return Objects.equals(session, that.session);
    }

    @Override
    public int hashCode() {
        return Objects.hash(session);
    }

    private static MyParams getXtxxParams(){
        if(xtxxParams==null){
            xtxxParams = LjqManager.jcxxByDxdm("SYS_PTGL_XTXX");
            xtxxParams.sys().setCllx(Cllx.insert.name());
        }
        return xtxxParams;
    }
}
